#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#define SIZE 512
#define END '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{p+=(strlen(p)-1);while(*p != '/') --p;}while(0)
#define None_Redir 0
#define In_Redir 1
#define Out_Redir 2
#define Append_Redir 3
#define SkipSpace(cmd,start) do{ while(cmd[start] == ' '){++start;} }while(0) 
    
char* filename = NULL;//重定向后的文件名
int Redir_Type = None_Redir;

void Die()
{
    exit(1);
}
const char* GetUserName()
{ 
     const char* username = getenv("USER");
     if(username == NULL) return "None";
     return username;
}
const char* GetHostName()
{
    const char* hostname  = getenv("HOSTNAME");
    if(hostname == NULL)return "None";
    return hostname;
}
const char* GetCwd()
{
    char* cwd = getenv("PWD");  
    if(cwd == NULL)return "None";
    return cwd;
}
void MakeCommandLine()
{
     char line[SIZE];
     const char* username = GetUserName();
     const char* hostname = GetHostName();
     const char* Cwd  = GetCwd();
     SkipPath(Cwd);
     snprintf(line,SIZE,"[%s@%s %s@]> ",username,hostname,strlen(Cwd) == 1? "/" : Cwd+1);
     printf("%s",line);
     fflush(stdout);
}
int GetUserCommand(char command[],size_t n)
{  
    char* str = fgets(command,n,stdin);
    if(str == NULL) return -1;
    //小细节：清空回车键导致的usercommand的最后元素的位置被改成的'\n'
    command[strlen(command)-1] = END;
    return strlen(command);
}
const char*GetHome()
{
     const char* home = getenv("HOME");
     return home;
}

char* gArgv[NUM];

void SplitCommand(char command[],size_t n)
{
     (void)n;
     //使用strtok函数
     gArgv[0] = strtok(command,SEP);
     int i = 1;
     while((gArgv[i++] = strtok(NULL,SEP)));
}
void EexCuteCommand()
{
    pid_t id = fork();
    if(id < 0) Die();
    else if(id == 0)
    {
        if(filename != NULL)
        {
            if(Redir_Type == In_Redir)
            {
                //输入重定向
                int fd = open(filename,O_RDONLY);
                if(fd < 0)
                {
                    perror("open");
                    exit(-1);
                }
                dup2(fd,0);
            }
            else if(Redir_Type == Out_Redir)
            {
                int fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
                if(fd < 0)
                {
                    perror("open");
                    exit(-2);
                }
                dup2(fd,1);
            }
            else if(Redir_Type ==Append_Redir)
            {
                int fd = open(filename,O_WRONLY | O_CREAT | O_APPEND,0666);
                if(fd < 0)
                {
                    perror("open");
                    exit(-3);
                }
                dup2(fd,1);
            }
            else {}
        }
      //child
      //_程序替换
      execvp(gArgv[0],gArgv);
      exit(errno);
    }
    else 
    {
      //father 
      int status = 0;
      pid_t rid = waitpid(id,&status,0);
      (void)rid;
    }
}

char cwd[SIZE*2];
void Cd()
{
     const char* path = gArgv[1];
     if(path == NULL ) path = GetHome();
     chdir(path);
     char tmpspacce[SIZE*2];
     getcwd(tmpspacce,sizeof tmpspacce);
     snprintf(cwd,sizeof cwd,"PWD=%s",tmpspacce);
     putenv(cwd);
}
int checkBuild()
{
    int YES= 0;
    const char*cmd = gArgv[0];
    if(strcmp(cmd,"cd") == 0)
    {
       YES = 1;
       Cd();
    }
    return YES;
}
void CheckRedirCmd(char cmd[])
{
    //检测CMD中是否存在> < >> 符号
    int start = 0,end = strlen(cmd);
    while(start < end)
    {
        if(cmd[start] == '>')
        {
            if(cmd[start+1] == '>')
            {
                cmd[start++] = '\0';
                ++start;
                Redir_Type = Append_Redir;
                SkipSpace(cmd,start);
            }
            else 
            {
                cmd[start++] = '\0';
                Redir_Type = Out_Redir;
                SkipSpace(cmd,start);
            }
            filename = cmd+start;
            break;
        }
        else if(cmd[start] == '<')
        {
            cmd[start++] = '\0';
            //更新状态
            Redir_Type = In_Redir;
            SkipSpace(cmd,start);
            filename = cmd + start;
            break;
        }
        else
        {
            ++start;
        }
    }
}
void Init()
{
    filename = NULL;
    Redir_Type = None_Redir;
}
int main()
{
    
    int quit = 0;
    while(!quit)
    {
        //初始化filename和重定向状态
        Init();
        //获取命令行
        MakeCommandLine();
        //获取指令字符串
        char usercommand[SIZE];
        int n = GetUserCommand(usercommand,sizeof usercommand);
        //检测是否是重定向指令
        CheckRedirCmd(usercommand);
        //分割命令行字符串的分割
        SplitCommand(usercommand,sizeof usercommand);
        //检测是否是内建命令
        n = checkBuild();
        if(n) continue;
        //执行命令
        EexCuteCommand();  
    }
    return 0;
}

